<?php
/**
 * @package mikrokosmos
 * file created on 5-mar-2006
 *
 * jaco_at_pixeldump_dot_org
 */

// sound definitions
$soundFormats = array();
$soundFormats["uncompressed"] = 0;
$soundFormats["adpcm"] = 1;
$soundFormats["mp3"] = 2;
$soundFormats["uncompressed_little_endian"] = 3;
$soundFormats["nellymoser"] = 6;

// PUBLISH values for sound/mp3 rate
$soundRates = array();
$soundRates[0] = 5500;
$soundRates[1] = 11000;
$soundRates[2] = 22000;
$soundRates[3] = 44000;

// EXACT values for mp3 rate
// they are useful for computing right samplesPerFrame value
$mp3Rates = array();
$mp3Rates[0] = 5512;
$mp3Rates[1] = 11025;
$mp3Rates[2] = 22050;
$mp3Rates[3] = 44100;

// bit depth for sample or sample pair
$sampleSizes = array();
$sampleSizes["bit8"] = 0;
$sampleSizes["bit16"] = 1;

// speaks itself
$soundTypes = array();
$soundTypes["mono"] = 0;
$soundTypes["stereo"] = 1;


//mp3 bitrate lookup table
	/*
	bits V1,L1 	V1,L2 	V1,L3 	V2,L1 	V2,L2 & L3
	0000 free	free	free	free	free
	0001 32 	32 		32 		32 		8
	0010 64 	48 		40		48		16
	0011 96 	56 		48 		56 		24
	0100 128 	64 		56 		64 		32
	0101 160 	80 		64 		80 		40
	0110 192 	96 		80 		96 		48
	0111 224 	112 	96 		112 	56
	1000 256 	128 	112 	128 	64
	1001 288 	160 	128 	144 	80
	1010 320 	192 	160 	160 	96
	1011 352 	224 	192 	176 	112
	1100 384 	256 	224 	192 	128
	1101 416 	320 	256 	224 	144
	1110 448 	384 	320 	256 	160
	1111 bad 	bad 	bad 	bad 	bad
	*/
$brLU = array();
$brLU["V1L1"] = array(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 284, 416, 488);
$brLU["V1L2"] = array(0, 32, 48, 56, 64,  80,  96,  112, 128, 160, 192, 224, 256, 320, 384);
$brLU["V1L3"] = array(0, 32, 40, 48, 56,  64,  80,  96,  112, 128, 160, 192, 224, 256, 320);
$brLU["V2L1"] = array(0, 32, 48, 56, 64,  80,  96,  112, 128, 144, 160, 176, 192, 224, 256);
$brLU["V2L2"] = array(0, 8,  16, 24, 32,  40,  48,  56,  64,  80,  96,  112, 128, 144, 160);
$brLU["V2L3"] = $brLU["V2L2"];


function get_sound_format($formatIdx){
	global $soundFormats;
	foreach($soundFormats as $k => $v)
		if($v == $formatIdx) return $k;
}

function get_sound_rate($rateIdx){
	global $soundRates;
	return $soundRates[$rateIdx];
}

function get_mp3_rate($rateIdx){
	global $mp3Rates;
	return $mp3Rates[$rateIdx];
}

function get_sampleSize($sizeIdx){
	return $sizeIdx == 0 ? "8bit" : "16bit";
}

function get_sound_type($typeIdx){
	return $typeIdx == 0 ? "mono" : "stereo";
}

function get_sound_compression($scIdx){
		$sndCmp = array();
		$sndCmp[SOUND_UNCOMPRESSED] = "uncompressed";
		$sndCmp[SOUND_ADPCM] = "adpcm";
		$sndCmp[SOUND_MP3] = "mp3";
		$sndCmp[SOUND_UNCOMPRESSED_LE] = "uncompressed little endian";		// uncompressed little-endian
		$sndCmp[SOUND_NELLYMOSER] = "nellymoser";
		return $sndCmp[$scIdx];
}

/**
 * reads data
 * fill an associative array with definesound fields
 */
function read_defineSound_data($buf = ""){

	if(!strlen($buf)) return false;	// avoid empty calls

	$sad = read_tag_header_data(substr($buf, 0, 10));
	$ofst = $sad["tagSL"] + 2;
	$soundFlags = get_binData_string($buf[$ofst]);
	$sad["format"] = bindec(substr($soundFlags, 0, 4));
	$sad["rate"] = bindec(substr($soundFlags, 4, 2));
	$sad["size"] = (int) $soundFlags[6];
	$sad["type"] = (int) $soundFlags[7];
	$ofst++;
	$sad["sampleCount"] = ui32dec(substr($buf, $ofst, 4));
	$ofst += 4;
	$sad["soundData"] = substr($buf, $ofst);

	return $sad;
}


/**
 * reads data
 * fill an associative array with SOUNDINFO fields
 */
function read_soundInfo_data($buf = ""){

	if(!strlen($buf)) return "";
	$sid = array();

	$siFlagStr = get_binData_string($buf[0]);

	$sid["syncStop"] 		= (int) $siFlagStr[2];
	$sid["syncNoMultiple"]  = (int) $siFlagStr[3];
	$sid["hasEnvelope"]  	= (int) $siFlagStr[4];
	$sid["hasLoops"]	  	= (int) $siFlagStr[5];
	$sid["hasOutPoint"]  	= (int) $siFlagStr[6];
	$sid["hasInPoint"]  	= (int) $siFlagStr[7];

	$ofst = 1;

	if($sid["hasInPoint"]){
		$sid["inPoint"] = ui32dec(substr($buf, $ofst, 2));
		$ofst += 4;
	}

	if($sid["hasOutPoint"]){
		$sid["outPoint"] = ui32dec(substr($buf, $ofst, 2));
		$ofst += 4;
	}

	if($sid["hasLoops"]){
		$sid["loopCount"] = ui16dec(substr($buf, $ofst, 2));
		$ofst += 2;
	}

	if($sid["hasEnvelope"]){

		$sid["envPoints"] = ui8dec($buf[$ofst]);
		$ofst ++;

		$erBuf = substr($buf, $ofst);
		$erCount = strlen($erBuf) / 8;

		$sid["envelopeRecords"] = array();
		for($i = 0, $j = 0; $i < $erCount; $i++, $j += 8){
			$sid["envelopeRecords"][$i] = read_soundEnvelope_data(substr($erBuf, $j, 8));
		}

		$ofst += strlen($erBuf);
	}

	$sid["siLength"] = $ofst;
	return $sid;
}

/**
 * reads data
 * fill an associative array with SOUNDENVELOPE fields
 */
function read_soundEnvelope_data($buf = ""){

	if(!strlen($buf)) return "";
	$sed = array();

	$ofst = 0;
	$sed["pos44"] = ui32dec(substr($buf, $ofst, 4));
	$ofst += 4;
	$sed["leftLevel"] = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;
	$sed["rightLevel"] = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;
	$sed["length"] = $ofst;

	return $sed;
}


/**
 * reads data
 * fill an associative array with soundStreamHead
 * OR soundStreamHead2 fields
 */
function read_soundStreamHead_data($buf = ""){

	if(!strlen($buf)) return false;	// avoid empty calls

	$shd = read_tag_header_data(substr($buf, 0, 10));
	$ofst = $shd["tagSL"];
	$shd["reserved"] = 0;
	$sshFlags = get_binData_string(substr($buf, $ofst, 2));

	$shd["pbRate"]   = bindec(substr($sshFlags, 4, 2));
	$shd["pbSize"]   = (int) $sshFlags[6];
	$shd["pbType"]   = (int) $sshFlags[7];

	$shd["ssCompression"] = bindec(substr($sshFlags, 8, 4));
	$shd["ssRate"]		  = bindec(substr($sshFlags, 12, 2));
	$shd["ssSize"]		  = (int) $sshFlags[14];
	$shd["ssType"]		  = (int) $sshFlags[15];

	$ofst += 2;
	$shd["ssSampleCount"] = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;
	if($shd["ssCompression"] == 2){
		$shd["latencySeek"]  = si16dec(substr($buf, $ofst, 2));
	}

	return $shd;
}

/**
 * writes data
 * compile and return definesound tagdata
 */
function write_defineSound_data($sad = array()){

	$tagBody = "";
	if(!count($sad)) return false;

	$tagBody = swf_word($sad["itemID"]);

	$flagStr = "";
	$flagStr .= swf_value2bin($sad["format"], 4);
	$flagStr .= swf_value2bin($sad["rate"], 2);
	$flagStr .= $sad["size"] ? "1" : "0";
	$flagStr .= $sad["type"] ? "1" : "0";

	$tagBody .= get_binData($flagStr);
	$tagBody .= swf_dword($sad["sampleCount"]);
	if($sad["format"] == SOUND_MP3) $tagBody .= chr(0x8D) .chr(0x06); // these are magic number don't know the meaning
	$tagBody .= $sad["soundData"];

	$tagHeader = SWFTag_long_header(SOUND, strlen($tagBody));

	return $tagHeader .$tagBody;
}


/**
 * writes data
 * compile soundInfo buf from a given associative
 */
function write_soundInfo_data($sid = array()){

	if(!count($sid)) return "";
	$siFlagStr = "00";
	$siFlagStr .= $sid["syncStop"] 		 ? "1" : "0";
	$siFlagStr .= $sid["syncNoMultiple"] ? "1" : "0";
	$siFlagStr .= $sid["hasEnvelope"] 	 ? "1" :"0";
	$siFlagStr .= $sid["hasLoops"] 		 ? "1" : "0";
	$siFlagStr .= $sid["hasOutPoint"] 	 ? "1" : "0";
	$siFlagStr .= $sid["hasInPoint"] 	 ? "1" : "0";

	$buf = swf_bin($siFlagStr);

	if($sid["hasInPoint"]){ $buf .= swf_dword($sid["inPoint"]); }
	if($sid["hasOutPoint"]){ $buf .= swf_dword($sid["outPoint"]); }
	if($sid["hasLoops"]){ $buf .= swf_word($sid["loopCount"]); }
	if($sid["hasEnvelope"]){
		$buf .= chr($sid["envPoints"]);
		foreach($sid["envelopeRecords"] as $er){
			$buf .= write_soundEnvelope_data($er);
		}
	}

	return $buf;
}

/**
 * writes data
 * compile binary data from a given associative array
 */
function write_soundEnvelope_data($sed = array()){

	$buf = swf_dword($sed["pos44"]);
	$buf .= swf_word($sed["leftLevel"]);
	$buf .= swf_word($sed["rightLevel"]);

	return $buf;
}


/**
 * writes data
 * compile soundStreamHead[2] tag
 */
function write_soundStreamHead_data($shd = array()){

	if(!count($shd)) return false;	// avoid empty calls

	$tagBody = "";

	$shd["reserved"] = 0;
	$sshFlags = "0000";
	$sshFlags .= swf_value2bin($shd["pbRate"], 2);
	$sshFlags .= $shd["pbSize"] ? "1" : "0";
	$sshFlags .= $shd["pbType"] ? "1" : "0";

	$sshFlags .= swf_value2bin($shd["ssCompression"], 4);
	$sshFlags .= swf_value2bin($shd["ssRate"], 2);
	$sshFlags .= $shd["ssSize"] ? "1" : "0";
	$sshFlags .= $shd["ssType"] ? "1" : "0";

	$tagBody .= get_binData($sshFlags);
	$tagBody .= swf_word($shd["ssSampleCount"]);

	if($shd["ssCompression"] == 2){
		$tagBody .= swf_word($shd["latencySeek"]);
	}

	$tagHeader = SWFTag_short_header($shd["tagID"], strlen($tagBody));

	return $tagHeader .$tagBody;
}


//// MP3 handling code


function read_mp3_data($buf, $mp3FileSize){

	$mp3d = array();

	$bufLength = strlen($buf);

	for($i = 0; $i < $bufLength; $i++){
		$fsTest = substr($buf, $i, 4);
		if(is_mp3_framesync($fsTest)){
			$mp3d = read_mp3Frame_header($fsTest, false);
			break;
		}
	}

	if(!count($mp3d)) return false; // no valid mp3 found

	$mp3d["format"] = SOUND_MP3;
	$mp3d["frameCount"] = floor(($mp3FileSize - $i) / $mp3d["frameSize"]);
	$mp3d["sampleCount"] = $mp3d["frameCount"] * 576;

	//if($mp3d["channelMode"] < 3)
	$mp3d["sampleCount"] *= 2;
	$mp3d["size"] = SOUND_16BIT;
	$mp3d["type"] = $mp3d["channelMode"] < 3 ? SOUND_STEREO : SOUND_MONO;

	return $mp3d;
}

/**
 * reads data
 * fill an associative array with mp3frame fields
 */
function read_mp3Frame_header($buf = "", $doTest = true){

	global $mp3Rates;
	if(!strlen($buf)) return false;

	// verify if is a valid frameSync
	$buf = substr($buf, 0, 4);
	if($doTest && !is_mp3_framsync($buf, 0, 2)) return false;

	$mFlags = get_binData_string($buf);

	$mfh = array();

	$mfh["version"]		  = bindec(substr($mFlags, 11, 2));
	$mfh["layer"]		  = bindec(substr($mFlags, 13, 2));
	$mfh["protectionBit"] = (int) $mFlags[15];

	$bitRate = translate_bitRate(bindec(substr($mFlags, 16, 4)), $mfh["version"], $mfh["layer"]);
	$rateValue = translate_rate(bindec(substr($mFlags, 20, 2)), $mfh["version"]);

	$mCount = count($mp3Rates);

	for($i = 0; $i < $mCount; $i++){
		if($rateValue == $mp3Rates[$i]) $rate = $i;
	}

	$mfh["bitRate"]		  = $bitRate;
	$mfh["rate"]		  = $rate;

	$mfh["padding"]		  = (int) $mFlags[22];
	$mfh["frameSize"]	  = floor(144 * $bitRate / $rateValue) + $mfh["padding"];
	$mfh["privateBit"]	  = (int) $mFlags[23];
	$mfh["channelMode"]	  = bindec(substr($mFlags, 24, 2));
	$mfh["modeExtension"] = bindec(substr($mFlags, 26, 2));
	$mfh["copyright"]	  = (int) $mFlags[28];
	$mfh["original"]	  = (int) $mFlags[29];
	$mfh["emphasis"]	  = bindec(substr($mFlags, 30, 2));

	return $mfh;
}

/**
 * converts mp3frame spec into a more readable (and usable) behavior
 * e.g.:
 */
function translate_bitRate($mp3BitRate, $mp3Version, $mp3Layer){

	global $brLU;
	// V2 here is equivalent to V25, let be the same to look correctly in brLU
	// the exactly indexes would be: "V25", "reserved", "V2", "V1"
	$versions = array("V2", "reserved", "V2", "V1");
	$layers = array("reserved", "L3", "L2", "L1");

	$idx = $versions[$mp3Version];
	$idx .= $layers[$mp3Layer];

	return $brLU[$idx][$mp3BitRate] * 1000;
}

/**
 * converts mp3 sampling rate to flash sampling rate
 */
function translate_rate($mp3rate, $mp3Version){

	global $mp3Rates;
	$versions = array("V25", "reserved", "V2", "V1");

	/*
	bits MPEG1 MPEG2 MPEG2.5
	00 	 44100 22050 11025
	01 	 48000 24000 12000
	10 	 32000 16000 8000
	11 	 reserv. reserv. reserv.
	*/

	$mp3R = array();
	$mp3R["V1"]  = array(44100, 48000, 32000);
	$mp3R["V2"]  = array(22050, 24000, 16000);
	$mp3R["V25"] = array(11025, 12000, 8000);

	$idx = $versions[$mp3Version];

	$rateValue = $mp3R[$idx][$mp3rate];

   	return  $rateValue; // index rate not found
}

/**
 * reads 2 byte data
 * verify if it's a valid framesync
 */
function is_mp3_framesync($fsTest){

	$fsSync = chr(0xFF) .chr(0xE0);

	return (substr($fsTest, 0, 2) & $fsSync) == $fsSync;
}

// WAV handling

/**
 * reads data
 * checks WAV valid header
 * returns an associative array with WAV header fields
 */
function read_wav_header($buf, $wavFileSize){

	$whd = array();
	$whd["error"] = "OK";

	// RIFF chunk descriptor
	$ofst = 0;
	$riffStr = substr($buf, $ofst, 4);
	if($riffStr != "RIFF"){
		$whd["error"] = "not a RIFF signature";
		return $whd;
	}

	$ofst += 4;
	$fSize = ui32dec(substr($buf, $ofst, 4)) + 8;
	if($fSize != $wavFileSize){
		$whd["error"] = "filesizes does not match";
		return $whd;
	}

	$ofst += 4;
	$waveStr = substr($buf, $ofst, 4);
	if($waveStr != "Wave"){
		$whd["error"] = "not a Wave signature";
		return $whd;
	}

	$ofst += 4;
	$fmtStr = substr($buf, $ofst, 4);
	if($fmtStr != "fmt "){
		$whd["error"] = "not a fmt signature";
		return $whd;
	}

	$ofst += 4;
	$whd["chunkSize"] = ui32dec(substr($buf, $ofst, 4));
	$ofst += 4;
	$whd["format"] = ui16dec(substr($buf, $ofst, 2));

	// fmt sub-chunk
	$ofst += 2;
	$whd["channels"] = ui16dec(substr($buf, $ofst, 2));
	$ofst += 2;
	$whd["spp"] = ui32dec(substr($buf, $ofst, 4));   // samples per second
	$ofst += 4;
	$whd["bsps"] = ui32dec(substr($buf, $ofst, 4));  // bytes sent per second
	$ofst += 4;
	$whd["bps"] = ui16dec(substr($buf, $ofst, 2));   // bytes per sample

	// data sub-chunk
	$ofst += 2;
	$dataStr = substr($buf, $ofst, 4);
	$ofst += 4;
	$whd["sampleCount"] = ui32dec(substr($buf, $ofst, 4));




	return $whd;
}

?>
